package com.link2room.rodimus.facade.impl;

import com.link2room.aeriabots.dto.result.base.WxMemberInfoResDto;
import com.link2room.common.constant.DefaultValue;
import com.link2room.common.dto.param.base.BaseL2rParamDto;
import com.link2room.common.dto.result.base.MemberSessionDto;
import com.link2room.common.dto.threadlocal.ApiThreadLocal;
import com.link2room.common.exception.SysException;
import com.link2room.common.jpa.condition.ConditionDetail;
import com.link2room.common.jpa.condition.L2RCondition;
import com.link2room.common.jpa.criteria.CriteriaUtil;
import com.link2room.common.jpa.result.QueryResult;
import com.link2room.common.service.ICommonService;
import com.link2room.common.util.*;
import com.link2room.rodimus.constant.CouponConstant;
import com.link2room.rodimus.constant.DrawConstant;
import com.link2room.rodimus.dto.param.api.CouponConsumeParamDto;
import com.link2room.rodimus.dto.param.api.DoDrawParamDto;
import com.link2room.rodimus.dto.param.api.DrawDeliverInfoParamDto;
import com.link2room.rodimus.dto.param.api.DrawLogsParamDto;
import com.link2room.rodimus.dto.param.base.DrawPrizeTypeDto;
import com.link2room.rodimus.dto.param.manager.*;
import com.link2room.rodimus.dto.result.base.*;
import com.link2room.rodimus.entity.*;

import com.link2room.rodimus.facade.IDrawFacadeService;
import com.link2room.rodimus.factory.AfterDrawHandlerFactory;
import com.link2room.rodimus.feign.aeriabots.WxMemberInfoFeignService;
import com.link2room.rodimus.service.IAfterDrawHandleService;
import com.link2room.rodimus.service.ICouponService;
import com.link2room.rodimus.service.IDrawService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.sql.Time;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

import static com.link2room.enums.exception.RodimusSysExceptionEnum.DRAW_ERROR;
import static com.link2room.enums.exception.RodimusSysExceptionEnum.DRAW_PRIZE_UPDATE_ERROR;
import static com.link2room.enums.exception.RodimusSysExceptionEnum.REDO_DRAW_AFTER_DO_ERROR;

/**
 * Created by LN on 2017/7/11.16:21
 */
@Component("drawFacadeService")
public class DrawFacadeServiceImpl implements IDrawFacadeService {
    private static Logger logger = LoggerFactory.getLogger(DrawFacadeServiceImpl.class);

    @Resource
    private ICommonService commonService;

    @Resource
    private IDrawService drawService;

    @Autowired
    private WxMemberInfoFeignService wxMemberInfoFeignService;
    @Resource
    private ICouponService couponService;

    /**
     * 抽奖记录线程池
     */
    private ExecutorService drawLogExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    @Override
    @Transactional
    public DoDrawResultDto doDrawResultNew(DoDrawParamDto paramDto) throws Exception {
        MemberSessionDto memberSession = ApiThreadLocal.getMemberSessionThreadLocal();
        long start = System.currentTimeMillis();
        String activityCode = paramDto.getActivityCode();
        L2RAssert.stringBlank(activityCode, "[activityCode] is null.");
        String lhotelGroupCode = paramDto.getLhotelGroupCode();
        String drawCode = paramDto.getDrawCode();
        long now = System.currentTimeMillis();
        //校验活动
        DrawActivityEntity drawActivityEntity = checkActivityDateAllowed(lhotelGroupCode, activityCode);
        //需要抽奖码，则检查抽奖码是否可用
        if (DefaultValue.T.equals(drawActivityEntity.getNeedDrawCode())) {
            L2RAssert.stringBlank(drawCode, "[drawCode] is null.");
            checkCodeHasUsed(activityCode, drawCode);
        }

        DrawPrizeBaseDto res = null;
        String drawNo = drawService.generateDrawCouponNo();
        boolean hasErro = false;
        try {
            System.out.println("活动校验耗时:" + String.valueOf(System.currentTimeMillis() - now));
            now = System.currentTimeMillis();
            CouponConsumeTempDto couponConsumRes = null;
            if (DefaultValue.T.equals(drawActivityEntity.getNeedCoupon())) {
                couponConsumRes = couponConsum(lhotelGroupCode, activityCode, memberSession.getOpenid());
            }
            res = drawService.doDraw(lhotelGroupCode, activityCode, drawCode,couponConsumRes);

            res.setDrawNo(drawNo);
            System.out.println("抽奖耗时:" + String.valueOf(System.currentTimeMillis() - now));
            //抽奖后做的事情
            now = System.currentTimeMillis();
            IAfterDrawHandleService afterDrawService = AfterDrawHandlerFactory.getAfterDrawHandleService(res.getPrizeType());
            if (afterDrawService != null) {
                afterDrawService.afterDraw(res);
            }
            System.out.println("抽奖后耗时:" + String.valueOf(System.currentTimeMillis() - now));

        }catch (Exception e){
            hasErro = true;
            throw e;
        }finally {
            if(!hasErro){
              DrawPrizeBaseDto finalRes = res;

              drawLogExecutorService.submit(() -> {
                try {
                  logDraw(finalRes, activityCode, drawCode, memberSession.getOpenid(), drawNo);
                } catch (Exception e) {
                  e.printStackTrace();
                }
              });
            }
        }
        DoDrawResultDto resultDto = new DoDrawResultDto();
        resultDto.setPrize(res);
        resultDto.setIsWon(DefaultValue.T);
        System.out.println("共耗时:" + String.valueOf(System.currentTimeMillis() - start));
        return resultDto;
    }


    private CouponConsumeTempDto couponConsum(String lhotelGroupCode, String activityCode, String openid) throws Exception {
        CouponGoodEntity find = new CouponGoodEntity();
        find.setLhotelGroupCode(lhotelGroupCode);
        find.setGoodCode(activityCode);
        CouponGoodEntity qres = commonService.findOneEQ(CouponGoodEntity.class, find);
        L2RAssert.isTrue(null == qres, new SysException(DRAW_ERROR, "此活动没有配置奖券.检查CouponGoodEntity. [activityCode] " + activityCode));
        L2RAssert.isTrue(DefaultValue.T.equals(qres.getIsHalt()), new SysException(DRAW_ERROR, "此活动奖券配置被禁用.检查CouponGoodEntity [activityCode]" + activityCode));
        CouponConsumeParamDto paramDto = new CouponConsumeParamDto();
        paramDto.setLhotelGroupCode(lhotelGroupCode);
        paramDto.setAmount(1);
        paramDto.setCouponCode(qres.getCouponCode());
        paramDto.setOpenid(openid);
        return couponService.couponConsumTemp(paramDto);
    }

    private void logDraw(DrawPrizeBaseDto res, String activityCode, String drawCode, String openid, String drawNo) throws Exception {
        if (res != null) {
            //log
            DrawLogEntity log = new DrawLogEntity();
            if ("NONE".equals(res.getPrizeType())) {
                log.setIsWon(DefaultValue.F);
            } else {
                log.setIsWon(DefaultValue.T);
            }
            log.setActivityCode(activityCode);
            log.setPrizeCode(res.getCode());
            log.setPrizeNo(res.getPrizeNo());
            log.setPrizeName(res.getName());
            log.setDrawCode(drawCode);
            log.setPrizeType(res.getPrizeType());
            log.setDrawNo(drawNo);
            log.setOpenid(openid);
            try {
                WxMemberInfoResDto wxMemberInfo = wxMemberInfoFeignService.getMemberInfos(res.getLhotelGroupCode(),openid).getResultInfo();
                if (wxMemberInfo != null) {
                    log.setParticipantName(wxMemberInfo.getNickname());
                }
                commonService.save(log);
            } catch (Exception e) {
                log.setParticipantName(null);
                commonService.save(log);
                System.out.println(ExceptionUtil.getExceptionDetail(e));
            }
        }
    }

    @Override
    public void createDrawCode() throws Exception {
        Set<String> codeSet = new HashSet<>(65536);
        while (codeSet.size() < 50500) {
            codeSet.add(getStringRandom(8));
        }
        List<DrawCodeEntity> saves = new ArrayList<>();
        codeSet.stream()
                .forEach(code -> {
                    DrawCodeEntity draw = new DrawCodeEntity();
                    draw.setCode(code);
                    draw.setIsUsed(DefaultValue.F);
                    draw.setLhotelGroupCode(DefaultValue.LHOTEL_GROUP_CODE);
                    saves.add(draw);
                });
        commonService.saveList(saves);
    }

    private DrawCodeEntity checkCodeHasUsed(String activityCode, String drawCode) throws Exception {
        DrawCodeEntity find = new DrawCodeEntity();
        find.setCode(drawCode);
        find.setActivityCode(activityCode);
        DrawCodeEntity drawCodeEntity = commonService.findOneEQ(DrawCodeEntity.class, find);
        L2RAssert.isTrue(drawCodeEntity == null, new SysException(DRAW_ERROR, "抽奖码不合法."));
        L2RAssert.isTrue(DefaultValue.T.equals(drawCodeEntity.getIsUsed()), new SysException(DRAW_ERROR, "抽奖码已经使用过."));
        return drawCodeEntity;
    }

    void useDrawCode(DrawCodeEntity drawCodeEntity) throws Exception {
        if (drawCodeEntity == null) {
            return;
        }

        drawCodeEntity.setIsUsed(DefaultValue.T);
        commonService.updateById(drawCodeEntity);
    }

    @Override
    public List<DrawLogDto> userDrawLogList(BaseL2rParamDto paramDto) throws Exception {
        MemberSessionDto memberSession = ApiThreadLocal.getMemberSessionThreadLocal();
        String openid = memberSession.getOpenid();
        DrawLogEntity find = new DrawLogEntity();
        find.setOpenid(openid);
        HashMap<String, String> orderBy = new HashMap<>();
        orderBy.put(DrawLogEntity.BaseEntity_.id.toString(), CriteriaUtil.DESC.toString());
        QueryResult<DrawLogEntity> qres = commonService.findAllEQ(DrawLogEntity.class, find, paramDto.getCurrentPage(), paramDto.getPageSize(), orderBy, null);
        if (qres != null && CollectionUtils.isNotEmpty(qres.getResultlist())) {
            return qres.getResultlist().stream()
                    .map(enitiy -> ClassConverUtil.copyProperties(enitiy, DrawLogDto.class))
                    .collect(Collectors.toList());
        } else {
            return Collections.emptyList();
        }
    }

    @Override
    public List<DrawLogDto> logs(DrawLogsParamDto paramDto) throws Exception {
        String isWon = paramDto.getIsWon();
        String type = paramDto.getType();
        String prizeCode = paramDto.getPrizeCode();
        L2RAssert.isTrue(
                StringUtil.isBlank(isWon) && StringUtil.isBlank(type) && StringUtil.isBlank(prizeCode),
                new SysException(DRAW_ERROR, "[isWon]和[type]和[prizeCode] 不能同时为null"));
        DrawLogEntity find = new DrawLogEntity();
        find.setIsWon(isWon);
        find.setPrizeType(type);
        find.setPrizeCode(prizeCode);
        find.setActivityCode(paramDto.getActivityCode());
        HashMap<String, String> orderBy = new HashMap<>();
        orderBy.put(DrawLogEntity.BaseEntity_.id.toString(), CriteriaUtil.DESC.toString());
        QueryResult<DrawLogEntity> qres = commonService.findAllEQ(DrawLogEntity.class, find, paramDto.getCurrentPage(), paramDto.getPageSize(), orderBy, null);
        if (qres != null && CollectionUtils.isNotEmpty(qres.getResultlist())) {
            return qres.getResultlist().stream()
                    .map(enitiy -> ClassConverUtil.copyProperties(enitiy, DrawLogDto.class))
                    .collect(Collectors.toList());
        } else {
            return Collections.emptyList();
        }
    }

    @Override
    public void addDeliverInfo(DrawDeliverInfoParamDto param) throws Exception {
        String address = param.getAddress();
        L2RAssert.stringBlank(address, "[address] is null.");
        String receiverName = param.getReceiverName();
        L2RAssert.stringBlank(address, "[receiverName] is null.");
        String mobile = param.getMobile();
        L2RAssert.stringBlank(address, "[receiverName] is null.");
        String lhotelGroupCode = param.getLhotelGroupCode();
        MemberSessionDto memberSession = ApiThreadLocal.getMemberSessionThreadLocal();
        String openid = memberSession.getOpenid();


        String prizeNo = param.getPrizeNo();
        L2RAssert.stringBlank(prizeNo, "[prizeNo] is null.");
        DrawLogEntity findLog = new DrawLogEntity();
        findLog.setPrizeNo(prizeNo);
        DrawLogEntity drawLog = commonService.findOneEQ(DrawLogEntity.class, findLog);
        L2RAssert.isTrue(drawLog == null, new SysException(DRAW_ERROR, prizeNo + "没有中奖记录"));
        L2RAssert.isTrue(DefaultValue.T.equals(drawLog.getHasDeliverInfo()), new SysException(DRAW_ERROR, prizeNo + "已经填写过收货地址"));

        DrawDeliverInfoEntity entity = new DrawDeliverInfoEntity();
        entity.setLhotelGroupCode(lhotelGroupCode);
        entity.setAddress(address);
        entity.setReceiverName(receiverName);
        entity.setMobile(mobile);
        entity.setPrizeNo(prizeNo);
        entity.setOpenid(openid);
        entity.setPrizeCode(drawLog.getPrizeCode());
        entity.setActivityCode(drawLog.getActivityCode());
        entity.setPrizeName(drawLog.getPrizeName());
        commonService.save(entity);
        drawLog.setHasDeliverInfo(DefaultValue.T);
        commonService.updateById(drawLog);
    }

    @Override
    public DrawDeliverInfoDto getDeliverInfoByPrizeNo(DrawDeliverInfoParamDto param) throws Exception {
        String prizeNo = param.getPrizeNo();
        L2RAssert.stringBlank(prizeNo, "[prizeNo] is null.");
        DrawDeliverInfoEntity find = new DrawDeliverInfoEntity();
        find.setPrizeNo(prizeNo);
        DrawDeliverInfoEntity qres = commonService.findOneEQ(DrawDeliverInfoEntity.class, find);
        if (qres == null) {
            return null;
        }
        return ClassConverUtil.copyProperties(qres, DrawDeliverInfoDto.class);
    }

    @Override
    public QueryResult<DrawActivityDto> activities(DrawActivitiesParamDto paramDto) throws Exception {
        String lhotelGroupCode = paramDto.getLhotelGroupCode();
        DrawActivityEntity find = new DrawActivityEntity();
        find.setLhotelGroupCode(lhotelGroupCode);
        QueryResult<DrawActivityEntity> qres = commonService.findAllEQ(DrawActivityEntity.class, find, paramDto.getCurrentPage(), paramDto.getPageSize(), null, null);
        if (qres != null && CollectionUtils.isNotEmpty(qres.getResultlist())) {
            return ClassConverUtil.copyQueryResultToDtoQueryResult(qres, DrawActivityDto.class, true);
        } else {
            QueryResult<DrawActivityDto> nullRes = new QueryResult<>();
            nullRes.setTotalrecord(0);
            nullRes.setCurrentPage(1);
            return nullRes;
        }
    }

    @Override
    public DrawActivityDto activitiy(DrawActivitiesParamDto paramDto) throws Exception {
        String code = paramDto.getCode();
        L2RAssert.stringBlank(code, "[activityCode] is required.");
        String lhotelGroupCode = paramDto.getLhotelGroupCode();
        L2RAssert.stringBlank(lhotelGroupCode, "[lhotelGroupCode] is required.");
        DrawActivityEntity find = new DrawActivityEntity();
        find.setCode(code);
        DrawActivityEntity qres = commonService.findOneEQ(DrawActivityEntity.class, find);
        QueryResult<DrawPrizeBaseDto> prizeBaseDto = this.findDrawPrizeBaseDto(lhotelGroupCode, code);
        if (qres == null) {
            return null;
        }
        DrawActivityDto res = ClassConverUtil.copyProperties(qres, DrawActivityDto.class);
        res.setDrawPrizeBaseList(prizeBaseDto.getResultlist());
        return res;
    }

    @Override
    public void updateActivity(DrawActivitiesUpdateParamDto paramDto) throws Exception {
        String lhotelGroupCode = paramDto.getLhotelGroupCode();
        L2RAssert.stringBlank(lhotelGroupCode, "[lhotelGroupCode] is null.");
        String code = paramDto.getCode();
        L2RAssert.stringBlank(code, "[code] is null.");
        Date endDate = paramDto.getEndDate();
        L2RAssert.isTrue(endDate == null, new SysException(DRAW_ERROR, "[endDate] is null."));
        Date startDate = paramDto.getStartDate();
        L2RAssert.isTrue(startDate == null, new SysException(DRAW_ERROR, "[startDate] is null."));
        L2RAssert.isTrue(startDate.after(endDate), new SysException(DRAW_ERROR, "[startDate] is after [endDate]."));
        Time startTime = paramDto.getActiveStartTime();
        L2RAssert.isTrue(startTime == null, new SysException(DRAW_ERROR, "[ActiveEndTime] is null."));
        LocalTime activityStart = LocalTime.parse(startTime.toString());
        Time endTime = paramDto.getActiveEndTime();
        L2RAssert.isTrue(endTime == null, new SysException(DRAW_ERROR, "[ActiveStartTime] is null."));
        LocalTime activityEnd = LocalTime.parse(endTime.toString());
        String name = paramDto.getName();
        L2RAssert.stringBlank(name, "[name] is null.");
        String remark = paramDto.getRemark();
        String description = paramDto.getDescription();
        String isHalt = paramDto.getIsHalt();
        L2RAssert.stringBlank(isHalt, "[isHalt] is null.");
        DrawActivityEntity find = new DrawActivityEntity();
        find.setCode(code);
        DrawActivityEntity toUpdate = commonService.findOneEQ(DrawActivityEntity.class, find);
        L2RAssert.isTrue(toUpdate == null, new SysException(DRAW_ERROR, "[code] is error.没有对应活动"));
        toUpdate.setActiveEndTime(endTime);
        toUpdate.setActiveStartTime(startTime);
        toUpdate.setDescription(description);
        toUpdate.setName(name);
        toUpdate.setRemark(remark);
        toUpdate.setStartDate(startDate);
        toUpdate.setEndDate(endDate);
        toUpdate.setIsHalt(isHalt);
        toUpdate.setObviouslyEndDate(paramDto.getObviouslyEndDate());
        toUpdate.setObviouslyStartDate(paramDto.getObviouslyStartDate());
        commonService.updateById(toUpdate);
    }

    @Override
    public void updateDrawPrizes(DrawPrizeUpdateParamDto paramDto) throws Exception {
        String activityCode = paramDto.getActivityCode();
        L2RAssert.stringBlank(activityCode, "[activityCode] is required.");
        String lhotelGroupCode = paramDto.getLhotelGroupCode();
        L2RAssert.stringBlank(lhotelGroupCode, "[lhotelGroupCode] is required.");
        DrawActivityEntity drawActivity = findDrawActivityEntity(lhotelGroupCode, activityCode);
        L2RAssert.isTrue(drawActivity == null, new SysException(DRAW_PRIZE_UPDATE_ERROR, "活动不存在.[activityCode] is " + activityCode));
        List<DrawPrizeBaseDto> drawPrizeBaseList = paramDto.getDrawPrizeBases();
        //drawPrizeBaseList 里面没有，直接删除
        if (CollectionUtils.isEmpty(drawPrizeBaseList)) {
            DrawPrizeBaseEntity deletePrize = new DrawPrizeBaseEntity();
            deletePrize.setLhotelGroupCode(lhotelGroupCode);
            deletePrize.setActivityCode(activityCode);
            commonService.deleteAllEQ(DrawPrizeBaseEntity.class, deletePrize);
        } else {

            Set<String> prizeBaseCodes = new HashSet<>();
            DrawPrizeBaseEntity findPrizeBase = new DrawPrizeBaseEntity();
            findPrizeBase.setLhotelGroupCode(lhotelGroupCode);
            findPrizeBase.setActivityCode(activityCode);
            QueryResult<DrawPrizeBaseEntity> qres = commonService.findAllEQ(DrawPrizeBaseEntity.class, findPrizeBase);
            List<DrawPrizeBaseEntity> updateEntities = drawPrizeBaseList
                    .stream()
                    .filter(this::checkPrizeBaseDto)
                    .filter(dto -> {
                        String code = dto.getCode();
                        if (prizeBaseCodes.contains(code)) {
                            throw new SysException(DRAW_PRIZE_UPDATE_ERROR, "[code] is duplicated . [code] is " + code);
                        } else {
                            return prizeBaseCodes.add(code);
                        }
                    })
                    .map(dto -> {
                        if (CollectionUtils.isEmpty(qres.getResultlist())) {
                            return createDrawPrizeBaseEntity(dto);
                        } else {
                            Optional<DrawPrizeBaseEntity> entityOptional = qres.getResultlist()
                                    .stream()
                                    .filter(entity ->
                                            entity.getCode().equals(dto.getCode())
                                    )
                                    .findFirst();
                            //若存在则更新，否则新建
                            if (entityOptional.isPresent()) {
                                return updateDrawPrizeBaseEntity(entityOptional.get(), dto);
                            } else {
                                return createDrawPrizeBaseEntity(dto);
                            }
                        }
                    })
                    .collect(Collectors.toList());
            List<DrawPrizeBaseEntity> deletes = qres.getResultlist().stream()
                    .filter(entity -> !prizeBaseCodes.contains(entity.getCode()))
                    .collect(Collectors.toList());
            drawService.updatePrizeBases(deletes, updateEntities);
        }
    }

    private DrawPrizeBaseEntity updateDrawPrizeBaseEntity(DrawPrizeBaseEntity entity, DrawPrizeBaseDto prizeBaseDto) {
        entity.setCode(prizeBaseDto.getCode());
        entity.setRule(prizeBaseDto.getRule());
        entity.setCurrentNum(prizeBaseDto.getCurrentNum());
        entity.setName(prizeBaseDto.getName());
        entity.setNumStrategy(prizeBaseDto.getNumStrategy());
        entity.setPrizeType(prizeBaseDto.getPrizeType());
        entity.setRate(prizeBaseDto.getRate());
        entity.setRule(prizeBaseDto.getRule());
        entity.setIsHalt(prizeBaseDto.getIsHalt());
        entity.setListOrder(prizeBaseDto.getListOrder());
        return entity;
    }

    private DrawPrizeBaseEntity createDrawPrizeBaseEntity(DrawPrizeBaseDto prizeBaseDto) {
        DrawPrizeBaseEntity newEntity = new DrawPrizeBaseEntity();
        newEntity.setLhotelGroupCode(prizeBaseDto.getLhotelGroupCode());
        newEntity.setLhotelCode(DefaultValue.LHOTEL_CODE);
        newEntity.setActivityCode(prizeBaseDto.getActivityCode());
        newEntity.setOccupied(0);
        newEntity.setCode(prizeBaseDto.getCode());
        newEntity.setCurrentNum(prizeBaseDto.getCurrentNum());
        newEntity.setName(prizeBaseDto.getName());
        newEntity.setNumStrategy(prizeBaseDto.getNumStrategy());
        newEntity.setPrizeType(prizeBaseDto.getPrizeType());
        newEntity.setRate(prizeBaseDto.getRate());
        newEntity.setRule(prizeBaseDto.getRule());
        return newEntity;
    }

    private boolean checkPrizeBaseDto(DrawPrizeBaseDto prizeBaseDto) {
        L2RAssert.isTrue(StringUtil.isBlank(prizeBaseDto.getName()), new SysException(DRAW_PRIZE_UPDATE_ERROR, "[name] is required."));
        L2RAssert.isTrue(StringUtil.isBlank(prizeBaseDto.getCode()), new SysException(DRAW_PRIZE_UPDATE_ERROR, "[code] is required."));
        L2RAssert.isTrue(StringUtil.isBlank(prizeBaseDto.getPrizeType()), new SysException(DRAW_PRIZE_UPDATE_ERROR, "[prizeType] is required."));
        L2RAssert.isTrue(
                !DrawConstant.PrizeType.pmsCoupon.equals(prizeBaseDto.getPrizeType()) &&
                        !DrawConstant.PrizeType.redpack.equals(prizeBaseDto.getPrizeType()) &&
                        !DrawConstant.PrizeType.digitalize.equals(prizeBaseDto.getPrizeType()) &&
                        !DrawConstant.PrizeType.ENTITY.equals(prizeBaseDto.getPrizeType()) &&
                        !DrawConstant.PrizeType.NONE.equals(prizeBaseDto.getPrizeType())
                ,
                new SysException(DRAW_PRIZE_UPDATE_ERROR, "[prizeType] is invalid."));
        L2RAssert.isTrue(StringUtil.isBlank(prizeBaseDto.getNumStrategy()), new SysException(DRAW_PRIZE_UPDATE_ERROR, "[numStrategy] is required."));
        L2RAssert.isTrue(
                !DrawConstant.NumStrategy.Limited.equals(prizeBaseDto.getNumStrategy()) &&
                        !DrawConstant.NumStrategy.unLimited.equals(prizeBaseDto.getNumStrategy())
                ,
                new SysException(DRAW_PRIZE_UPDATE_ERROR, "[numStrategy] is invalid."));
        L2RAssert.isTrue(prizeBaseDto.getNum() == null || prizeBaseDto.getNum() < 0, new SysException(DRAW_PRIZE_UPDATE_ERROR, "[num] is invalid."));
        L2RAssert.isTrue(prizeBaseDto.getRate() == null || prizeBaseDto.getRate() <= 0, new SysException(DRAW_PRIZE_UPDATE_ERROR, "[rate] is invalid."));
        L2RAssert.isTrue(prizeBaseDto.getCurrentNum() == null || prizeBaseDto.getCurrentNum() < 0, new SysException(DRAW_PRIZE_UPDATE_ERROR, "[currentNum] is invalid."));
        L2RAssert.ishaltVaild(prizeBaseDto.getIsHalt());
        return true;
    }

    @Override
    public List<DrawJackpotDto> getDrawJackDto(DrawJackpotUpdateParamDto paramDto) throws Exception {
        String activityCode = paramDto.getActivityCode();
        L2RAssert.stringBlank(activityCode, "[activityCode] is null.");
        DrawActivityEntity findActivity = new DrawActivityEntity();
        findActivity.setCode(activityCode);
        DrawActivityEntity activityEntity = commonService.findOneEQ(DrawActivityEntity.class, findActivity);
        L2RAssert.isTrue(activityEntity == null, new SysException(DRAW_PRIZE_UPDATE_ERROR, "[activityCode ] " + activityCode + " 没有对应活动"));
        String querySql = jackQuerySql(activityCode);
        QueryResult<DrawJackpotDto> qres = commonService.findByNative(
                querySql,
                null,
                Collections.EMPTY_LIST,
                Collections.EMPTY_LIST,
                DrawJackpotDto.class,
                null,
                null);

        return qres.getResultlist();
    }

    @Override
    public void jackpotUpdate(DrawJackpotUpdateParamDto paramDto) throws Exception {
        String lhotelGroupCode = paramDto.getLhotelGroupCode();
        L2RAssert.stringBlank(lhotelGroupCode, "[lhotelGroupCode] is null.");
        String activityCode = paramDto.getActivityCode();
        L2RAssert.stringBlank(activityCode, "[activityCode] is null.");
        String prizeCode = paramDto.getPrizeCode();
        L2RAssert.stringBlank(prizeCode, "[prizeCode] is null.");
        Integer num = paramDto.getNum();
        L2RAssert.numberNull(num, "[availableCount] is null.");

        drawService.jackPotUpdate(lhotelGroupCode, activityCode, prizeCode, num);

    }

    @Override
    public List<DrawDeliverInfoDto> getDrawDeliverInfoList(DrawDeliverInfoParamDto paramDto) throws Exception {
        String lhotelGroupCode = paramDto.getLhotelGroupCode();
        L2RAssert.stringBlank(lhotelGroupCode, "[lhotelGroupCode] is null.");
        String activityCode = paramDto.getActivityCode();
        L2RAssert.stringBlank(activityCode, "[activityCode] is null.");
        String isDelivered = paramDto.getIsDelivered();
        DrawDeliverInfoEntity find = new DrawDeliverInfoEntity();
        find.setLhotelGroupCode(lhotelGroupCode);
        find.setActivityCode(activityCode);
        find.setIsDelivered(isDelivered);
        QueryResult<DrawDeliverInfoEntity> qres = commonService.findAllEQ(DrawDeliverInfoEntity.class, find, paramDto.getCurrentPage(), paramDto.getPageSize(), null, null);
        if (qres == null || CollectionUtils.isEmpty(qres.getResultlist())) {
            return Collections.emptyList();
        } else {
            return qres.getResultlist().stream()
                    .map(entity -> ClassConverUtil.copyProperties(entity, DrawDeliverInfoDto.class))
                    .collect(Collectors.toList());
        }
    }

    private QueryResult<DrawPrizeBaseDto> findDrawPrizeBaseDto(String lhotelGroupCode, String activityCode) throws Exception {

        L2RAssert.stringBlank(activityCode, "[activityCode] is required.");
        L2RAssert.stringBlank(lhotelGroupCode, "[lhotelGroupCode] is required.");
        DrawPrizeBaseEntity find = new DrawPrizeBaseEntity();
        find.setActivityCode(activityCode);
        find.setLhotelGroupCode(lhotelGroupCode);
        QueryResult<DrawPrizeBaseEntity> qres = commonService.findAllEQ(DrawPrizeBaseEntity.class, find);
        QueryResult<DrawPrizeBaseDto> res = ClassConverUtil.copyQueryResultToDtoQueryResult(qres, DrawPrizeBaseDto.class, true);
        return res;
    }

    @Override
    public void updateDrawDeliverInfo(DrawDeliverInfoParamDto paramDto) throws Exception {
        String prizeNo = paramDto.getPrizeNo();
        L2RAssert.stringBlank(prizeNo, "[prizeNo] is null.");
        String thirdNo = paramDto.getThirdNo();
        L2RAssert.stringBlank(thirdNo, "[thirdNo] is null.");
        String company = paramDto.getCourierServicesCompany();
        L2RAssert.stringBlank(company, "[company] is null.");
        DrawDeliverInfoEntity find = new DrawDeliverInfoEntity();
        find.setPrizeNo(prizeNo);
        String isD = paramDto.getIsDelivered();
        L2RAssert.stringBlank(isD, "[isDelivered] is null.");
        DrawDeliverInfoEntity qres = commonService.findOneEQ(DrawDeliverInfoEntity.class, find);
        L2RAssert.isTrue(qres == null, new SysException(DRAW_ERROR, "没有发货信息"));
        qres.setIsDelivered(isD);
        qres.setCourierServicesCompany(company);
        qres.setThirdNo(thirdNo);
        commonService.updateById(qres);
    }

    @Override
    public Long findDrawChanceCount(DoDrawParamDto paramDto) throws Exception {
        MemberSessionDto memberSession = ApiThreadLocal.getMemberSessionThreadLocal();
        String activityCode = paramDto.getActivityCode();
        CouponGoodEntity find = new CouponGoodEntity();
        find.setGoodCode(activityCode);
        find.setLhotelGroupCode(paramDto.getLhotelGroupCode());
        CouponGoodEntity qres = commonService.findOneEQ(CouponGoodEntity.class, find);
        logger.info("a抽奖次数统计:paramDto "+ paramDto + " qres:"+ qres + " memberSession" + memberSession + " couponService :"+commonService);
        Long count = couponService.findCouponCount(paramDto.getLhotelGroupCode(), qres.getCouponCode(), memberSession.getOpenid());
        return count;
    }

    @Override
    public QueryResult<DrawPrizeTypeDto> findPrizeBaseType(BaseL2rParamDto paramDto) throws Exception {
        String lhotelGroupCode = paramDto.getLhotelGroupCode();
        DrawPrizeTypeEntity find = new DrawPrizeTypeEntity();
        find.setLhotelGroupCode(lhotelGroupCode);
        find.setIsHalt(paramDto.getIsHalt());
        QueryResult<DrawPrizeTypeEntity> qres = commonService.findAllEQ(DrawPrizeTypeEntity.class, find, paramDto.getCurrentPage(), paramDto.getPageSize());
        return ClassConverUtil.copyQueryResultToDtoQueryResult(qres,DrawPrizeTypeDto.class,true);
    }


    @Override
    public void redoDrawAfter(DrawAfterDoRedoParamDto paramDto) throws Exception {
        List<String> drawNos = paramDto.getDrawNos();
        String activityCode = paramDto.getActivityCode();
        L2RAssert.stringBlank(activityCode, "[activityCode] is required.");
        if (CollectionUtils.isEmpty(drawNos)) {
            drawNos = Collections.EMPTY_LIST;
        }
        L2RCondition findLogCondition = new L2RCondition();
        findLogCondition.setAndConditions(
                new ConditionDetail[]{
                        new ConditionDetail(DrawAfterDoLogEntity.DrawAfterDoLogEntity_.resultCode.toString(), CriteriaUtil.Operator.EQ, DrawConstant.DrawAfterDo.RESULT_CODE.FAILED),
                        new ConditionDetail(DrawAfterDoLogEntity.DrawAfterDoLogEntity_.activityCode.toString(), CriteriaUtil.Operator.EQ, activityCode),
                        new ConditionDetail(DrawAfterDoLogEntity.DrawAfterDoLogEntity_.drawNo.toString(), CriteriaUtil.Operator.IN, drawNos),
                }
        );
        QueryResult<DrawAfterDoLogEntity> qres = commonService.findAllByCondition(DrawAfterDoLogEntity.class, findLogCondition);
        if (CollectionUtils.isNotEmpty(qres.getResultlist())) {
            Set<String> prizeCodeSet = qres.getResultlist().stream()
                    .map(DrawAfterDoLogEntity::getPrizeCode)
                    .collect(Collectors.toSet());
            L2RCondition findPrizeBaseCondition = new L2RCondition();
            findPrizeBaseCondition.setAndConditions(
                    new ConditionDetail[]{
                            new ConditionDetail(DrawPrizeBaseEntity.DrawPrizeBaseEntity_.activityCode.toString(), CriteriaUtil.Operator.EQ, activityCode),
                            new ConditionDetail(DrawPrizeBaseEntity.DrawPrizeBaseEntity_.code.toString(), CriteriaUtil.Operator.IN, prizeCodeSet),
                    }
            );
            QueryResult<DrawPrizeBaseEntity> prizeBases = commonService.findAllByCondition(DrawPrizeBaseEntity.class, findPrizeBaseCondition);
            L2RAssert.isTrue(CollectionUtils.isEmpty(prizeBases.getResultlist()), new SysException(REDO_DRAW_AFTER_DO_ERROR, "没有查询到相应奖项." + "[activityCode] " + activityCode + " [prizeCode] " + prizeCodeSet));
            Map<String, List<DrawPrizeBaseEntity>> przieMap = prizeBases.getResultlist().stream().collect(Collectors.groupingBy(DrawPrizeBaseEntity::getCode));
            qres.getResultlist()
                    .forEach(log -> {
                                DrawPrizeBaseEntity prizeBase = przieMap.get(log.getPrizeCode()).get(0);
                                IAfterDrawHandleService afterDrawService = AfterDrawHandlerFactory.getAfterDrawHandleService(prizeBase.getPrizeType());
                                if(afterDrawService != null){
                                    afterDrawService.redoAfterDraw(log.getDrawNo(), prizeBase);
                                }
                            }
                    );
        } else {
            return;
        }
    }

    private String jackQuerySql(String activityCode) {
        String querySQL = "SELECT\n" +
                "    a.lhotel_group_code lhotelGroupCode,\n" +
                "    a.activity_code activityCode,\n" +
                "    a.prize_code prizeCode,\n" +
                "    a.prize_name prizeName,\n" +
                "  COUNT(*) allCount,\n" +
                "  COUNT(IF(a.is_occupied='T',TRUE,NULL)) occupiedCount ,\n" +
                "  COUNT(IF(a.is_occupied='F' AND is_halt='F',TRUE,NULL))  notOccupiedCount ,\n" +
                "  COUNT(IF(a.is_occupied='F',TRUE,NULL)) availableCount  \n" +
                "FROM\n" +
                "  (SELECT\n" +
                "    lhotel_group_code,\n" +
                "    activity_code,\n" +
                "    prize_code,\n" +
                "    prize_name,\n" +
                "    is_occupied,\n" +
                "    is_halt\n" +
                "  FROM\n" +
                "    `draw_jackpot`\n" +
                "  WHERE activity_code = '" + activityCode + "')  a\n" +
                "GROUP BY activity_code,\n" +
                "  prize_code";
        return querySQL;
    }

    //生成随机数字和字母,
    private String getStringRandom(int length) {
        int CHAR = 1;
        int NUM = 0;
        String val = "";
        Random random = new Random();

        int charCount = 0;
        int numCount = 0;
        //参数length，表示生成几位随机数
        for (int i = 0; i < length; i++) {

            int charOrNum = 0;
            if (charCount + numCount == 0) {
                charOrNum = CHAR;
            } else if (charCount >= length / 2) {
                charOrNum = NUM;
            } else if (numCount >= length / 2) {
                charOrNum = CHAR;
            } else {
                charOrNum = random.nextInt(2) % 2 == 0 ? CHAR : NUM;
            }

            //输出字母还是数字
            if (CHAR == charOrNum) {
                charCount++;
                int temp = 65;
                //去掉I L O
                int v = 73;
                for (; v == 73 || v == 76 || v == 79; ) {
                    v = random.nextInt(26) + temp;
                }
                val += (char) (v);
            } else if (NUM == charOrNum) {
                numCount++;
                //去掉 0 1
                int v = 0;
                for (; v == 0 || v == 1; ) {
                    v = random.nextInt(10);
                }
                val += String.valueOf(v);
            }
        }
        return val;
    }

    /**
     * 检查是否符合抽检活动时间,符合则返回抽奖活动
     *
     * @param lhotelGroupCode
     * @param activityCode
     * @return
     * @throws Exception
     */
    private DrawActivityEntity checkActivityDateAllowed(String lhotelGroupCode, String activityCode) throws Exception {
        DrawActivityEntity find = new DrawActivityEntity();
        find.setCode(activityCode);
        find.setLhotelGroupCode(lhotelGroupCode);
        DrawActivityEntity drawEntity = commonService.findOneEQ(DrawActivityEntity.class, find);
        L2RAssert.isTrue(drawEntity == null, new SysException(DRAW_ERROR, "活动不存在."));
        L2RAssert.isTrue(DefaultValue.T.equals(drawEntity.getIsHalt()), new SysException(DRAW_ERROR, "活动已停用.",null));
        Date startDate = drawEntity.getStartDate();
        L2RAssert.isTrue(startDate == null, new SysException(DRAW_ERROR, "startDate is null.检查配置.",null));
        Date endDate = drawEntity.getEndDate();
        L2RAssert.isTrue(endDate == null, new SysException(DRAW_ERROR, "endDate is null.检查配置.",null));
        String needMember = drawEntity.getNeedMember();
        if (DefaultValue.T.equals(needMember)) {
            MemberSessionDto memberSession = ApiThreadLocal.getMemberSessionThreadLocal();
            L2RAssert.isTrue(!DefaultValue.T.equals(memberSession.getIsMember()), new SysException(DRAW_ERROR, "非微信会员不能抽奖.",null));
        }
        String needBindPms = drawEntity.getNeedBindPms();
        if (DefaultValue.T.equals(needBindPms)) {
            MemberSessionDto memberSession = ApiThreadLocal.getMemberSessionThreadLocal();
            L2RAssert.isTrue(!DefaultValue.T.equals(memberSession.getIsbindPms()), new SysException(DRAW_ERROR, "未绑定pms不能抽奖.",null));
        }
        Date now = new Date();
        boolean isInDate = now.after(startDate) && now.before(endDate);
        L2RAssert.isTrue(
                !isInDate,
                new SysException(DRAW_ERROR, "不在活动日期。活动日期:[" + DateUtil.format(startDate, "yyyy-MM-dd HH:mm:ss") + "到]"
                        + DateUtil.format(endDate, "yyyy-MM-dd HH:mm:ss"),null)
        );
        LocalTime time = LocalTime.now();
        LocalTime startTime = drawEntity.getActiveStartTime().toLocalTime();
        LocalTime endTime = drawEntity.getActiveEndTime().toLocalTime();
        if (startTime == null || endTime == null) {

        } else {
            boolean isInTime;
            if (endTime.compareTo(startTime) == 0) {
                return drawEntity;
            } else if (endTime.isBefore(startTime)) {
                isInTime = time.isAfter(startTime) || time.isBefore(endTime);
            } else {
                isInTime = time.isAfter(startTime) && time.isBefore(endTime);
            }
            L2RAssert.isTrue(
                    !isInTime,
                    new SysException(DRAW_ERROR, "不在活动时间。时间:[" + startTime + "到]"
                            + endTime, null)
            );
        }
        return drawEntity;
    }

    private DrawActivityEntity findDrawActivityEntity(String lhotelGroupCode, String activityCode) throws Exception {
        DrawActivityEntity find = new DrawActivityEntity();
        find.setCode(activityCode);
        find.setLhotelGroupCode(lhotelGroupCode);
        DrawActivityEntity qres = commonService.findOneEQ(DrawActivityEntity.class, find);
        return qres;
    }


	@Override
	public DoDrawResultDto doDrawResultWithPrizeCode(DoDrawParamDto paramDto) throws Exception {
		MemberSessionDto memberSession = ApiThreadLocal.getMemberSessionThreadLocal();
        long start = System.currentTimeMillis();
        String activityCode = paramDto.getActivityCode();
        L2RAssert.stringBlank(activityCode, "[activityCode] is null.");
        String lhotelGroupCode = paramDto.getLhotelGroupCode();
        long now = System.currentTimeMillis();
        //校验活动
        DrawActivityEntity drawActivityEntity = checkActivityDateAllowed(lhotelGroupCode, activityCode);

        DrawPrizeBaseDto res = null;
        String drawNo = drawService.generateDrawCouponNo();
        boolean hasErro = false;
        try {
            System.out.println("活动校验耗时:" + String.valueOf(System.currentTimeMillis() - now));
            now = System.currentTimeMillis();
            CouponConsumeTempDto couponConsumRes = null;
            if (DefaultValue.T.equals(drawActivityEntity.getNeedCoupon())) {
                couponConsumRes = couponConsum(lhotelGroupCode, activityCode, memberSession.getOpenid());
            }
            res = drawService.doDrawWithPrizeCode(lhotelGroupCode, activityCode ,paramDto.getPrizeCode(), couponConsumRes);

            res.setDrawNo(drawNo);
            System.out.println("抽奖耗时:" + String.valueOf(System.currentTimeMillis() - now));
            //抽奖后做的事情
            now = System.currentTimeMillis();
            IAfterDrawHandleService afterDrawService = AfterDrawHandlerFactory.getAfterDrawHandleService(res.getPrizeType());
            if (afterDrawService != null) {
                afterDrawService.afterDraw(res);
            }
            System.out.println("抽奖后耗时:" + String.valueOf(System.currentTimeMillis() - now));

        }catch (Exception e){
            hasErro = true;
            throw e;
        }finally {
            if(!hasErro){
              DrawPrizeBaseDto finalRes = res;

              drawLogExecutorService.submit(() -> {
                try {
                  logDraw(finalRes, activityCode, null, memberSession.getOpenid(), drawNo);
                } catch (Exception e) {
                  e.printStackTrace();
                }
              });
            }
        }
        DoDrawResultDto resultDto = new DoDrawResultDto();
        resultDto.setPrize(res);
        resultDto.setIsWon(DefaultValue.T);
        System.out.println("共耗时:" + String.valueOf(System.currentTimeMillis() - start));
        return resultDto;
	}


	@Override
	public Long initDrawChanceCount(DoDrawParamDto paramDto) throws Exception {
		MemberSessionDto memberSession = ApiThreadLocal.getMemberSessionThreadLocal();
        String activityCode = paramDto.getActivityCode();
        CouponGoodEntity find = new CouponGoodEntity();
        find.setGoodCode(activityCode);
        find.setLhotelGroupCode(paramDto.getLhotelGroupCode());
        CouponGoodEntity qres = commonService.findOneEQ(CouponGoodEntity.class, find);
        Long count = couponService.findCouponCountAll(paramDto.getLhotelGroupCode(), qres.getCouponCode(), memberSession.getOpenid());
        if(count <= 0) {
        	CouponDetailEntity newCoupon = couponService.initNewCoupon(qres.getCouponCode(), qres.getLhotelGroupCode(), memberSession.getOpenid(), "FREE", null);
        	newCoupon.setMemberNo(memberSession.getMemberNo());
        	commonService.saveOrUpdate(newCoupon);
        }
        return couponService.findCouponCount(paramDto.getLhotelGroupCode(), qres.getCouponCode(), memberSession.getOpenid());
	}
}
